#ifndef CALLONCE_H
#define CALLONCE_H

#include <QAtomicInt>
#include <QThread>
#include <QThreadStorage>

namespace CallOnce
{
    enum CallOnceState { CallOnceEmpty, CallOnceInProgress, CallOnceFinished };

    Q_GLOBAL_STATIC(QThreadStorage<QAtomicInt*>, callOnceFlag)

    template <typename Function>
    inline static void callOnce(Function func, QBasicAtomicInt& flag)
    {
        int protectFlag = flag.fetchAndStoreAcquire(flag.loadAcquire());
        if (protectFlag == CallOnceFinished)
            return;
        if (protectFlag == CallOnceEmpty && flag.testAndSetRelaxed(protectFlag, CallOnceInProgress)) {
            func();
            flag.fetchAndStoreRelease(CallOnceFinished);
        }
        else {
            do {
                QThread::yieldCurrentThread();
            }
            while (!flag.testAndSetAcquire(CallOnceFinished, CallOnceFinished));
        }
    }

    template <typename Function>
    inline static void callOncePerThread(Function func)
    {
        if (!callOnceFlag()->hasLocalData()) {
            callOnceFlag()->setLocalData(new QAtomicInt(CallOnceEmpty));
            callOnce(func, *callOnceFlag()->localData());
        }
    }
}

#endif // CALLONCE_H
